#! /usr/local/bin/python
"""
Author: Andy Nagels
Date: 2010-02-24
Clipf2db: Insert clipf financial data into db.

Copyright (C) 2010 Andy Nagels

This file is part of Clipf2db.

Clipf2db is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Clipf2db is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Clipf2db. If not, see <http://www.gnu.org/licenses/>.
					
"""

import sys, getopt, ConfigParser, time, os, psycopg2 as dbapi2
from subprocess import call
from string import strip
from datetime import datetime
from databaseaccess import DatabaseAccess


class Clipf2db:
    
    def get_pprog(self):
        """ pprog """
        return self._pprog

    def set_pprog(self, name):
        """ set pprog """
        self._pprog = name

    pprog = property(get_pprog, set_pprog)

    def get_pversion(self):
        """ pversion """
        return self._pversion

    def set_pversion(self, version):
        """ set pversion """
        self._pversion = version

    pversion = property(get_pversion, set_pversion)
     
    def get_prelease(self):
        """ prelease """
        return self._prelease

    def set_prelease(self, release):
        """ set prelease """
        self._prelease = release

    prelease = property(get_prelease, set_prelease)
     
    def get_pdate(self):
        """ pdate """
        return self._pdate

    def set_pdate(self, date):
        """ set pdate """
        self._pdate = date

    pdate = property(get_pdate, set_pdate)
     
    def get_exitstate(self):
        """ Run or exit program? """
        return self._exitstate

    def set_exitstate(self, state):
        """ Set run-/exitstate """
        self._exitstate = state

    exitstate = property(get_exitstate, set_exitstate)

    def get_mydata(self):
        """ mydata """
        return self._mydata

    def set_mydata(self, version):
        """ set mydata """
        self._mydata = version

    mydata = property(get_mydata, set_mydata)

    def get_commentfields(self):
        """ commentfields """
        return self._commentfields

    def set_commentfields(self, value):
        """ set commentfields """
        self._commentfields = value

    commentfields = property(get_commentfields, set_commentfields)

    def get_insupdmode(self):
        """ insupdmode """
        return self._insupdmode

    def set_insupdmode(self, value):
        """ set insupdmode """
        self._insupdmode = value

    insupdmode = property(get_insupdmode, set_insupdmode)

    def __init__(self):
        """ Init of main class """
        self.set_pprog('clipf2db.py')
        self.set_pversion('0.02a')
        self.set_prelease('Traveler')
        self.set_pdate('2010-08-22')
        self.set_exitstate(0)
        self.set_mydata(self.DataDir())
        self.msgHandler = __import__('messagehandler')

    def __del__(self):
        """ del of main class """
        if self.get_exitstate() == 0:
            print 'Done.'
         
    def DataDir(self):
        """ Get data file path from XDG_DATA_DIR. """
        # TODO: get from env var
        return '/home/rockwolf/.config/clipf/db/op'
        #return 'config/clipf2db/testdb'

    def Usage(self):
        """ Print usage info and exit """
        print '''{0} : Cli application that inserts clipf data in a database.
Options: 
 -h : displays this help message
 -f : use the financial information of this file
      (overrides the one in the config file!)
 --setup : creates a table in the database, for storing our financial data... it will not do anything if the table already exists 
 --remove : deletes all tables in the database, all data will be destroyed...
 --version : displays version
 --python : displays Python version

Running the program without arguments, will have it use the default
~/.config/clipf2db/db/op file.'''.format(self.get_pprog())
        self.set_exitstate(1)

    def Run(self):
        """ This is the main driver for the program. """
        if self.get_exitstate() == 1:
            sys.exit(0)
        # Check if tables are empty and set insert or update mode accordingly
        self.CheckEmpty()
        # Convert financial data to db.
        self.ParseFile()
        sys.exit(0)

    def Setup(self):
        """ Setup the db. """
        dba = DatabaseAccess()
        dba.Setup()
        dba = None
    
    def Remove(self):
        """ Remove the tables + data from the db. """
        dba = DatabaseAccess()
        dba.Remove()
        dba = None
         
    def ParseFile(self):
        """ Parse textfile and insert data in db. """
        try:
            source = open(self.get_mydata())
            lines = source.readlines()
            fields = {}
            
            dba = DatabaseAccess()
            print self.get_mydata() + ' -> ' + dba.get_dbname() + ': '
            dba = None
           
            print '[ ', 
            for line in lines:
                fields = line.split(':')
                fields_db = {
                    'date':fields[0],
                    'acc':fields[1],
                    'prod':fields[2],
                    'amount':fields[3],
                    'flag':fields[4],
                    'comment':fields[5].replace('\'','\\\'')
                }
                self.ProcessLines(fields_db)
            print '\b\b|]'
        except IOError as (errno, strerror):
            print "Error {0}: {1} at {2}.".format(errno, strerror, self.get_mydata())
        finally:
            source.close()

    def CheckEmpty(self):
        """ Check if the tables are empty. """
        self.set_insupdmode(0) #insert mode is standard
        dba = DatabaseAccess()
        self.set_insupdmode(dba.CheckEmpty())
        dba

    def ProcessLines(self, fields_db):
        """ Processing the lines of the input file. """
        #print 'Starting process...'
        #print ' ',
        if self.get_insupdmode() == 0:
            self.CopyFile2DB(fields_db)
            self.ParseComments(fields_db)
            cmnt = self.get_commentfields()
            if cmnt != {}:
                # we have comment fields
                if 'mcode' in cmnt:
                    # They are stocks
                    self.ConvertStocks(self.get_commentfields())
                    self.ConvertStocksCurrent(self.get_commentfields())
                elif 'teamA' in cmnt:
                    # They are bets
                    self.ConvertBets(self.get_commentfields()) 
                    # Keep some information about current bets in a seperate table
                    # TODO: This is an extra feat, that will be finished later, when the important funcs are done.
                    #fConvertBetsCurrent(db, fields_comment_db)
        elif self.get_insupdmode() == 1:
            #print 'dummy',
            dummy = 1 #do nothing yet (but don't print)
            # Calls for updating do not work yet. It is a completely seperate part, so we
            # can use the program already (with --remove and --setup).
            #    print 'Table ' + self.tblfinance + ' is NOT empty: updating/appending values.'
            #    # We might be updating data or we might be adding new fields
            #    self.CopyFile2DBUpdate(db, fields_db) 
            #    self.ParseComments(db, fields_db)
            #    if self.get_commentfields() != {}:
            #        self.ConvertStocksUpdate(db, self.get_commentfields())
            #        self.ConvertStocksCurrentUpdate(db, self.get_commentfields())
            #        self.ConvertBetsUpdate(db, self.get_commentfields()) 
            #        # Keep some information about current bets in a seperate table
            #        # TODO: This is an extra feat, that will be finished later, when the important funcs are done.
            #        #fConvertBetsCurrentUpdate(db, fields_comment_db)
        else:
            print 'Error: insupdmode unknown.'

    def CopyFile2DB(self, fields_db):
        """ Convert general financial information. """
        dba = DatabaseAccess()
        dba.CopyFile2DB(fields_db)
        dba = None

    def CopyFile2DBUpdate(self, db, fields_db):
        """ Toevoegen van nieuwe data. """
        #TODO: db parm weg en het systeem anders maken, zodat
        # de cur hier ni moet, maar in databaseaccess.py
        cur = db.cursor()
        if cur.fetchone() != None:
            tid = cur.fetchone()[0]
            tdate = cur.fetchone()[1]
            tacc = cur.fetchone()[2]
            tprod = cur.fetchone()[3]
            tamount = cur.fetchone()[4]
            tflag = cur.fetchone()[5]
            tcomment = cur.fetchone()[6]
            ttags = cur.fetchone()[7]
            # TODO: convert dates to datetime format for comparison
            #print 'test: ' + datetime.strptime(tdate, "%Y-%m-%d")
            sys.exit(0)
            dateconv = datetime.strptime(fields_db['date'], "%Y-%m-%d")
            tdateconv = datetime.strptime(tdate, "%Y-%m-%d")
            if dateconv >= tdateconv and (fields_db['acc'] != tacc or fields_db['prod'] != tprod or fields_db['amount'] != tamount):
                # Only new data is added
                self.CopyFile2DB(db, fields_db)
                self.ParseComments(db, fields_db)
            elif fields_db['date'] <= tdate and fields_db['acc'] == tacc and fields_db['prod'] == tprod and fields_db['amount'] == tamount and (fields_db['flag'] != tflag or fields_db['comment'] != tcomment): #or fields_db['tags'] != ttags):
                # Update function goes here. Beware: changes to the pk fields can not be done automatically! Always make sure the amount is correct. If not, manually adjust it in the db!
                self.UpdateFinance(db, fields_db)
                self.UpdateSpecials(db, fields_db)
            #else:
                # We do nothing, because it's a field that has already been added before
        else:
            self.CopyFile2DB(db, fields_db)
            self.ParseComments(db, fields_db)

    def ParseComments(self, fields_db):
        """ Convert financial entries that abuse the comment field for more functionality. """
        fields_comment = []
        fields_comment_db = {}
        
        if fields_db['prod'] == 'invest.buystocks' or fields_db['prod'] == 'invest.changestocks' or fields_db['prod'] == 'invest.sellstocks':
            # stocks
            name = ''
            mcode = ''
            action = ''
            price = '0'
            qty = '0'
            fields_comment = fields_db['comment'].split(',')
            name = str(fields_comment[0]).split('.')[1]
            mcode = fields_comment[0].split('.')[0]
            qty = fields_comment[1]
            price = fields_comment[2]
            action = fields_db['prod'].split('.')[1][:-6] #buy/sell/change
            #print 'test: action =' + action
            fields_comment_db = {
                'name': name,
                'mcode': mcode,
                'action': action,
                'price': price,
                'qty': qty,
            }
        elif fields_db['prod'] == 'bet.place' or fields_db['prod'] == 'bet.cashin':
            # bets
            teamA = ''
            teamB = ''
            choice = ''
            pool = 0.0
            scoreA = -1
            scoreB = -1
            fields_comment = fields_db['comment'].split(',')
            teamA = fields_comment[0].split('vs.')[0].strip()
            teamB = fields_comment[0].split('vs.')[1].strip()
            pool = fields_db['amount'].strip()
            if fields_db['prod'] == 'bet.place':
                choice = fields_comment[1].strip().upper()
                score = ''
                scoreA = '-1'
                scoreB = '-1'
            else:
                score = fields_comment[1].strip()
                scoreA = score.split('-')[0].strip()
                scoreB = score.split('-')[1].strip()
                if (float(pool) > 0 and int(scoreA) > int(scoreB)) or (float(pool) == 0 and int(scoreB) > int(scoreA)):
                    choice = 'A'
                elif (float(pool) > 0 and int(scoreB) > int(scoreA)) or (float(pool) == 0 and int(scoreA) > int(scoreB)):
                    choice = 'B'
                else:
                    #This should not happen, unless money is paid back when the scores are equal
                    #or when a bet is cancelled. In this case, the choice doesn't matter anymore.
                    choice = ''
            fields_comment_db = {
                'teamA': teamA,
                'teamB': teamB,
                'choice': choice,
                'pool': pool,
                'scoreA': scoreA,
                'scoreB': scoreB,
            }
        self.set_commentfields(fields_comment_db)

    def ConvertStocks(self, fields_comment_db):
        """" Convert general stock information. """
        dba = DatabaseAccess()
        dba.ConvertStocks(fields_comment_db)
        dba = None

    def ConvertStocksCurrent(self, fields_comment_db):
        """ Upgrade table with current stock information. """
        dba = DatabaseAccess()
        dba.ConvertStocksCurrent(fields_comment_db)
        dba = None

    def ConvertBets(self, fields_comment_db):
        """ Convert general betting information. """
        dba = DatabaseAccess()
        dba.ConvertBets(fields_comment_db)
        dba = None

    def ConvertBetsCurrent(self, fields_comment_db):
        """ Upgrade table with current betting information. """
        dba = DatabaseAccess()
        dba.ConvertBetsCurrent(fields_comment_db)   
        dba = None
 
    def UpdateFinance(self, fields_db):
        """ Update financial data. """
        print 'Dummy print statement for updating finance info'

    def UpdateSpecials(self, fields_db):
        """ Update stock info + betting info. """
        print 'Dummy print statement for updating specials'
        
def Main():
    """ Main driver, startup and cli options parsing. """
    # Gonna switch this to optparse later
    try:
        options, xarguments = getopt.getopt(sys.argv[1:], 'hf:', ['setup', 'remove', 'version', 'python'])
    except getopt.error,err:
        print 'Error: ' + str(err)
        sys.exit(1)

    cl = Clipf2db()

    for opt in options[:]:
        if opt[0] == '-h':
            cl.Usage()
            cl.set_exitstate(1) # don't run the program after the optionparsing
    for opt in options[:]:
        if opt[0] == '-f':
            cl.set_mydata(opt[1].strip())
            break
    for opt in options[:]:
        if opt[0] == '--setup':
            cl.Setup()
            cl.set_exitstate(1)
            break
    for opt in options[:]:
        if opt[0] == '--remove':
            cl.Remove()
            cl.set_exitstate(1)
            break
    for opt in options[:]:
        if opt[0] == '--version':
            print cl.get_pprog() + ' version '+ cl.get_pversion() + ' (' + cl.get_pdate() + '), \'' + cl.get_prelease() + '\' release.'
            cl.set_exitstate(1)
            break
    for opt in options[:]:
        if opt[0] == '--python':
            print 'Python '+sys.version
            cl.set_exitstate(1)
            break

    cl.Run() #run the main method for the program

if __name__ == "__main__":
    Main()
